/****************************************************************************
 *
 *  ALi (Shanghai) Corporation, All Rights Reserved. 2002 Copyright (C)
 *
 *  File: UpgradeCommand.cpp
 *
 *  Description: implement functions interface about upgrade commands
 *		         
 *  History:
 *      Date		Author      Version		Comment
 *      ====        ======      =======		=======
 *  1   2007.8.23	Tony Wang	1.0			Create
 ****************************************************************************/

#include <types.h>
#include <sys_config.h>

#include <api/libc/printf.h>
#include <api/libc/string.h>
#include <api/libc/fastCRC.h>
#include <api/libchunk/chunk.h>
#include <bus/sci/sci.h>
#include <bus/flash/flash.h>
#include <hld/hld_dev.h>

#include "Packet.h"
#include "ErrorCode.h"
#include "UpgradeCommand.h"

static PACKET packet;

unsigned char g_packet_repeat = 0;
UINT32 g_packet_index = 0;

extern UINT g_protocol_version;

#define COMTEST_DATA "@ALI PROTOCOL 3.0+"


/********************************* Local Function Define *********************************/

/****************************************************************************
 *
 *  Name:		ExecuteCommand
 *	Function:	Execute a command 
 *	variable:	szCommand		-- command line string
 *				nTimeOut		-- time out 
 *				
 *				
 *  
 ****************************************************************************/
UINT32 ExecuteCommand(char * szCommand, UINT32 nTimeOut, unsigned char *pStatus)
{
	UINT32 nReturn = SUCCESS;
	BOOL bRepeat = FALSE;
//	PACKET packet;
	UINT32 i = 0;
	for(; i < COMMAND_RETRY_TIMES; i++)
	{
		nReturn = SendCommandPacket(szCommand, bRepeat);
		if(SUCCESS == nReturn)
		{
			nReturn = packet_receive(&packet, nTimeOut);
			if(SUCCESS == nReturn)
			{
				if(packet.packet_type == PACKET_STATUS &&
					packet.packet_index == g_packet_index &&
					packet.packet_length == STATUS_DATA_LEN)
				{
					if(pStatus != NULL)
						*pStatus = packet.data_buffer[0];
					if(packet.data_buffer[0] == COMMAND_STATUS_OK ||
						packet.data_buffer[0] == COMMAND_STATUS_RUNNING ||
						packet.data_buffer[0] == COMMAND_STATUS_EXECUTED)
						break;
					else
					{
						nReturn = fetch_long(packet.data_buffer + 1);
						// if error can't repeat break
						if(nReturn >= RETRY_CODE_MAX)
							break;	
					}
				}
				else
					nReturn = ERROR_PACKET_NOTRECOGNIZE;
			}
		}
		bRepeat = TRUE;
	}
	return nReturn;
}

/****************************************************************************
 *
 *  Name:		ExecuteCommandEx
 *	Function:	Execute a command and return data  
 *	variable:	szCommand		-- command line string
 *				nTimeOut		-- time out 
 *				
 *				
 *  
 ****************************************************************************/
UINT32 ExecuteCommandEx(char * szCommand, UINT32 nTimeOut, unsigned char *pStatus, 
					  UINT32 *pDataLen, unsigned char *pBuffer)
{
	UINT32 nReturn = SUCCESS;
	BOOL bRepeat = FALSE;
//	PACKET packet;

	ASSERT(pDataLen);
	ASSERT(pBuffer);

	com_clear_buffer();	
	UINT32 i = 0;
	for(; i < COMMAND_RETRY_TIMES; i++)
	{
		nReturn = SendCommandPacket(szCommand, bRepeat);
		if(SUCCESS == nReturn)
		{
			nReturn = packet_receive(&packet, nTimeOut);
			if(SUCCESS == nReturn)
			{
				if(packet.packet_index == g_packet_index)
				{
					if(packet.packet_type == PACKET_STATUS)	
					{
						if(pStatus != NULL)
							*pStatus = packet.data_buffer[0];
						if(packet.data_buffer[0] == COMMAND_STATUS_EXECUTED)
						{
							//if command is executed but pc not receive data re execute by new index
							bRepeat = FALSE;		
							continue;
						}
						else if(packet.data_buffer[0] == COMMAND_STATUS_ERROR)
						{
							nReturn = fetch_long(packet.data_buffer + 1);
							// if error can't repeat break
							if(nReturn >= RETRY_CODE_MAX)
								break;	
						}
						else
						{
							//status must not be RUNNING || OK 
							ASSERT(0);	//
						}
					}
					else if(packet.packet_type == PACKET_DATA)
					{
						nReturn = SUCCESS;
						*pDataLen = packet.packet_length;
						MEMCPY(pBuffer, packet.data_buffer, *pDataLen);
						break;
					}
				}
		
			}
		}
		bRepeat = TRUE;
	}
	return nReturn;
}
/********************************* Function Define *********************************/
/****************************************************************************
 *
 *  Name:		clear_packet_index
 *	Function:	clear global packet index
 *	variable:	
 *  
 ****************************************************************************/
void clear_packet_index()
{
	g_packet_index = 0;
	g_packet_repeat = 0; 
}

/****************************************************************************
 *
 *  Name:		cmd_version
 *	Function:	command version return chunk info
 *	variable:	version 	-- get protocol version about STB
 *				
 *				
 *  
 ****************************************************************************/
UINT32 cmd_version_new(UINT32 *version)
{
	UINT32  nLen = 0;
	unsigned char szBuffer[MAX_PACKET_SIZE];

	char szCommand[256] = "version\r";

	unsigned char nStatus = 0;

	MEMSET(szBuffer, 0, MAX_PACKET_SIZE);

	UINT32	nReturn = ExecuteCommandEx(szCommand, 1000, &nStatus, &nLen, szBuffer);
	if(SUCCESS == nReturn)
		*version = fetch_long(szBuffer + 4);
	return nReturn;
		
}

/****************************************************************************
 *
 *  Name:		cmd_chunk
 *	Function:	command chunk return chunk info
 *	variable:	chunk_info 		-- chunk info about command version return
 *				
 *				
 *  
 ****************************************************************************/
extern GetChunk(BYTE *buffer, UINT32 nLen);
UINT32 cmd_chunk_new()
{
	UINT32  nLen = 0;
	unsigned char szBuffer[MAX_PACKET_SIZE];

	char szCommand[256] = "chunk\r";

	unsigned char nStatus = 0;

	MEMSET(szBuffer, 0, MAX_PACKET_SIZE);

	UINT32	nReturn = ExecuteCommandEx(szCommand, 5 * 1000, &nStatus, &nLen, szBuffer);
	if(SUCCESS == nReturn)
		GetChunk(szBuffer + 4, nLen - 4);
	
	return nReturn;
}

/****************************************************************************
 *
 *  Name:		cmd_address
 *	Function:	set start address
 *	variable:	addr -- start address
 *				
 *				
 *  
 ****************************************************************************/
UINT32 cmd_address_new(UINT32 addr)
{
	char szCommand[256];

	sprintf(szCommand, "address %u\r", addr);

	return ExecuteCommand(szCommand, 1000, NULL);
}

/****************************************************************************
 *
 *  Name:		cmd_move
 *	Function:	move chunk
 *	variable:	index -- chunk index
 *				offset - chunk offset
 *				
 *  
 ****************************************************************************/
UINT32 cmd_move_new(UINT32 index,UINT32 offset)
{
	char szCommand[256];

	sprintf(szCommand, "move %u %u\r", index, offset);


	//clean read buffer
	return ExecuteCommand(szCommand, 10 * 1000, NULL);	
}

/****************************************************************************
 *
 *  Name:		cmd_burn
 *	Function:	burn flash
 *	variable:	pfProgress 		-- callback function about progress
 *				
 *								
 *  
 ****************************************************************************/
UINT32 cmd_burn_new(int nBurnSize, CALLBACK_PROGRESS pfProgress, BOOL bCareReturn)
{

	char szCommand[256] = "burn\r";

	UINT32 nReturn = ExecuteCommand(szCommand, 1000, NULL);
	if(SUCCESS != nReturn)
		return nReturn;

	if(!bCareReturn)
		return SUCCESS;
	
//	PACKET		packet;
	int nErrCount = 0;
	while(nErrCount < 5)
	{
		//receive status packet
		nReturn = packet_receive(&packet, 10 * 1000);
		if(SUCCESS == nReturn)
		{
			// if packet is recognize packet check packet data
			// else send request packet to request status in STB
			if(packet.packet_index == g_packet_index)
			{
				if(packet.packet_type == PACKET_STATUS)
				{
					if(packet.data_buffer[0] == COMMAND_STATUS_EXECUTED) 
					{
						//burn has completed
						(*pfProgress)(100);
						nReturn = SUCCESS;	
						break;
					}
					else if(packet.data_buffer[0] == COMMAND_STATUS_ERROR)//error
					{
						nReturn = fetch_long(packet.data_buffer + 1);
						break;
					}
					else if(packet.data_buffer[0] == COMMAND_STATUS_RUNNING)
					{
						nErrCount = 0;
						continue;			
					}
				}
				else if(packet.packet_type == PACKET_DATA)
				{
					if(packet.data_buffer[4] == 'B')
					{
						packet.data_buffer[9] = '\0';
						int nCmdLen = atoi((char *)packet.data_buffer + 5) * 1024;
						int nProcess = nCmdLen * 100 / nBurnSize;
						if(nProcess < 0 )
							nProcess = 0;
						else if (nProcess > 100)
							nProcess = 100;
						(*pfProgress)(nProcess);			
					}
					else if(packet.data_buffer[4] == 'F')
					{
						(*pfProgress)(100);
						nReturn = SUCCESS;
						break;
					}
					else if(packet.data_buffer[4] == 'E')
					{
						packet.data_buffer[12]='\0';
						return ERROR_BURN;
					}
				}
			}	
		}
		else
			nErrCount++;
	}	
	
	if(nErrCount >= 5)
		nReturn = ERROR_OVERLOADRETRYTIME;
	return nReturn;
}


/****************************************************************************
 *
 *  Name:		cmd_burn_new_ext
 *	Function:	burn flash, with abort funtion
 *	variable:	pfProgress 		-- callback function about progress
 *				
 *								
 *  
 ****************************************************************************/
UINT32 cmd_burn_new_ext(int nBurnSize, CALLBACK_PROGRESS pfProgress, BOOL bCareReturn, CALLBACK_USERABORT pfUserAbort)
{

	char szCommand[256] = "burn\r";

	UINT32 nReturn = ExecuteCommand(szCommand, 1000, NULL);
	if(SUCCESS != nReturn)
		return nReturn;

	if(!bCareReturn)
		return SUCCESS;
	
//	PACKET		packet;
	int nErrCount = 0;
	while(nErrCount < 5)
	{
		//check if user cancel data transfer
		if(pfUserAbort != NULL && (*pfUserAbort)() == TRUE)
			return ERROR_USERABORT;
		
		//receive status packet
		nReturn = packet_receive(&packet, 10 * 1000);
		if(SUCCESS == nReturn)
		{
			// if packet is recognize packet check packet data
			// else send request packet to request status in STB
			if(packet.packet_index == g_packet_index)
			{
				if(packet.packet_type == PACKET_STATUS)
				{
					if(packet.data_buffer[0] == COMMAND_STATUS_EXECUTED) 
					{
						//burn has completed
						(*pfProgress)(100);
						nReturn = SUCCESS;	
						break;
					}
					else if(packet.data_buffer[0] == COMMAND_STATUS_ERROR)//error
					{
						nReturn = fetch_long(packet.data_buffer + 1);
						break;
					}
					else if(packet.data_buffer[0] == COMMAND_STATUS_RUNNING)
					{
						nErrCount = 0;
						continue;			
					}
				}
				else if(packet.packet_type == PACKET_DATA)
				{
					if(packet.data_buffer[4] == 'B')
					{
						packet.data_buffer[9] = '\0';
						int nCmdLen = atoi((char *)packet.data_buffer + 5) * 1024;
						int nProcess = nCmdLen * 100 / nBurnSize;
						if(nProcess < 0 )
							nProcess = 0;
						else if (nProcess > 100)
							nProcess = 100;
						(*pfProgress)(nProcess);			
					}
					else if(packet.data_buffer[4] == 'F')
					{
						(*pfProgress)(100);
						nReturn = SUCCESS;
						break;
					}
					else if(packet.data_buffer[4] == 'E')
					{
						packet.data_buffer[12]='\0';
						return ERROR_BURN;
					}
				}
			}	
		}
		else
			nErrCount++;
	}	
	
	if(nErrCount >= 5)
		nReturn = ERROR_OVERLOADRETRYTIME;
	return nReturn;
}



/****************************************************************************
 *
 *  Name:		cmd_transfer
 *	Function:	move chunk
 *	variable:	buffer - data buffer for transfer
 *				nBufLen	-- buffer len
 *				pfProgress 		-- callback function about progress
 *				pfUserAbort 	-- callback function about if user abort thread
 *  
 ****************************************************************************/
UINT32 cmd_transfer_new(unsigned char *buffer, int nBufLen, 
		CALLBACK_PROGRESS pfProgress,  CALLBACK_USERABORT pfUserAbort)
{
	char szCommand[256];

	sprintf(szCommand, "transfer %d\r", nBufLen);


	//send transfer command and check status
	UINT32 nReturn = ExecuteCommand(szCommand, 1000, NULL);
	if(SUCCESS != nReturn)
		return nReturn;

	
	int nErrCount = 0;
	unsigned char *pos;	
		
	if(ALI_S3281==sys_ic_get_chip_id())
		pos = (unsigned char *)(buffer);
	else
		pos = (unsigned char *)(buffer-((unsigned char *)FLASH_BASE));

	UINT32 nTransLen = PACKAGESIZE;
    unsigned char data_buffer[PACKAGESIZE];
	BOOL bRequest = FALSE;
	UINT32 nLen = 0;
	UINT32	nPacketNum = 1;
//	PACKET packet;


	while(nLen < (UINT32)nBufLen && nErrCount < TRANSFER_RETRY_TIMES)
	{
		//check if user cancel data transfer
		if(pfUserAbort != NULL && (*pfUserAbort)() == TRUE)
			return ERROR_USERABORT;
		
		if((nLen + PACKAGESIZE) > (UINT32)nBufLen)
			nTransLen = nBufLen - nLen;
        	MEMSET(data_buffer,0xFF,PACKAGESIZE);

		if(ALI_S3281==sys_ic_get_chip_id())
			memcpy(data_buffer, pos, nTransLen);
		else
			sto_get_data((struct sto_device*)dev_get_by_id(HLD_DEV_TYPE_STO, 0),data_buffer,(UINT32)pos,(INT32)nTransLen);

		if(bRequest)
			nReturn = SendRequestPacket(nPacketNum);
		else
			nReturn = SendDataPacket(nPacketNum, data_buffer, nTransLen);
	

		if(SUCCESS == nReturn)
		{
			//receive status packet
			nReturn = packet_receive(&packet, 1500);
			
			if(SUCCESS == nReturn)
			{
				// if packet is recognize packet check packet data
				// else send request packet to request status in STB
				if(packet.packet_type == PACKET_STATUS &&
					packet.packet_index ==g_packet_index)
				{
					if(packet.data_buffer[0] == COMMAND_STATUS_OK)
					{
						UINT32 nNum = fetch_long(packet.data_buffer + 1);

						bRequest = FALSE;
						nErrCount = 0;
						if(nNum == nPacketNum)
						{
							//current transfer ok send next packet 
							nPacketNum ++;
							nLen += nTransLen;
							pos += nTransLen;
							nErrCount = 0;
							
							(*pfProgress)(nTransLen);
						}
						//else resend current packet
						continue;
					}
					else if(packet.data_buffer[0] == COMMAND_STATUS_EXECUTED) 
					{
						//transfer has completed
						(*pfProgress)(nTransLen);
						nReturn = SUCCESS;
						break;
					}
					else if(packet.data_buffer[0] == COMMAND_STATUS_ERROR)//error
					{
						nReturn = fetch_long(packet.data_buffer + 1);
						if(nReturn >= RETRY_CODE_MAX)
							break;
					}
				}

			}	
		}
	

				// retry to link
		bRequest = TRUE;
		nErrCount++;
		continue;	
	}
	if(nErrCount >= TRANSFER_RETRY_TIMES)
		nReturn = ERROR_OVERLOADRETRYTIME;
	return nReturn;
}

/****************************************************************************
 *
 *  Name:		cmd_reboot
 *	Function:	reboot target
 *	variable:	
 *				
 *				
 *  
 ****************************************************************************/
UINT32 cmd_reboot_new()
{

	char szCommand[256] = "reboot\r";

	return ExecuteCommand(szCommand, 1000, NULL);
}

